package com.hanser.hancoder.core.builder;

import cn.hutool.core.util.RuntimeUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 构建 Vue 项目
 */
@Slf4j
@Component
public class VueProjectBuilder {

    /**
     * npm命令路径，可通过配置文件指定
     */
    @Value("${vue-builder.npm-path:}")
    private String npmPath;

    /**
     * 异步构建 Vue 项目
     *
     * @param projectPath 项目根目录路径
     */
    public void buildProjectAsync(String projectPath) {
        Thread.ofVirtual().name("vue-builder-" + System.currentTimeMillis())
                .start(() -> {
                    try {
                        buildProject(projectPath);
                    } catch (Exception e) {
                        log.error("异步构建 Vue 项目时发生异常: {}", e.getMessage(), e);
                    }
                });
    }

    /**
     * 构建 Vue 项目
     *
     * @param projectPath 项目根目录路径
     * @return 是否构建成功
     */
    public boolean buildProject(String projectPath) {
        File projectDir = new File(projectPath);
        if (!projectDir.exists() || !projectDir.isDirectory()) {
            log.error("项目目录不存在：{}", projectPath);
            return false;
        }
        // 检查是否有 package.json 文件
        File packageJsonFile = new File(projectDir, "package.json");
        if (!packageJsonFile.exists()) {
            log.error("项目目录中没有 package.json 文件：{}", projectPath);
            return false;
        }
        log.info("开始构建 Vue 项目：{}", projectPath);
        // 执行 npm install
        if (!executeNpmInstall(projectDir)) {
            log.error("npm install 执行失败：{}", projectPath);
            return false;
        }
        // 执行 npm run build
        if (!executeNpmBuild(projectDir)) {
            log.error("npm run build 执行失败：{}", projectPath);
            return false;
        }
        // 验证 dist 目录是否生成
        File distDir = new File(projectDir, "dist");
        if (!distDir.exists() || !distDir.isDirectory()) {
            log.error("构建完成但 dist 目录未生成：{}", projectPath);
            return false;
        }
        log.info("Vue 项目构建成功，dist 目录：{}", projectPath);
        return true;
    }

    /**
     * 执行 npm install 命令
     */
    private boolean executeNpmInstall(File projectDir) {
        log.info("执行 npm install...");
        String command = String.format("%s install", buildCommand());
        return executeCommand(projectDir, command, 300); // 5分钟超时
    }

    /**
     * 执行 npm run build 命令
     */
    private boolean executeNpmBuild(File projectDir) {
        log.info("执行 npm run build...");
        String command = String.format("%s run build", buildCommand());
        return executeCommand(projectDir, command, 180); // 3分钟超时
    }

    /**
     * 根据操作系统构造命令
     *
     * @return 命令字符串
     */
    private String buildCommand() {
        // 如果配置了npm路径，直接使用
        if (npmPath != null && !npmPath.trim().isEmpty()) {
            log.debug("使用配置的npm路径: {}", npmPath);
            return npmPath.trim();
        }
        
        // 在Linux系统下，尝试查找npm路径
        if (!isWindows()) {
            String npmCommand = findNpmCommand();
            if (npmCommand != null) {
                log.debug("找到npm命令: {}", npmCommand);
                return npmCommand;
            }
        }
        
        // 默认行为
        if (isWindows()) {
            return "npm" + ".cmd";
        }
        return "npm";
    }

    /**
     * 在Linux系统下查找npm命令路径
     *
     * @return npm命令路径，如果找不到则返回null
     */
    private String findNpmCommand() {
        // 常见的npm路径
        String[] possiblePaths = {
                "/usr/bin/npm",
                "/usr/local/bin/npm",
                "/bin/npm"
        };
        
        // 检查用户主目录下的NVM安装
        String homeDir = System.getProperty("user.home");
        if (homeDir != null) {
            File nvmDir = new File(homeDir, ".nvm/versions/node");
            if (nvmDir.exists() && nvmDir.isDirectory()) {
                File[] versionDirs = nvmDir.listFiles(File::isDirectory);
                if (versionDirs != null && versionDirs.length > 0) {
                    // 使用最新版本的node
                    File latestVersion = versionDirs[versionDirs.length - 1];
                    String nvmNpmPath = latestVersion.getAbsolutePath() + "/bin/npm";
                    if (new File(nvmNpmPath).exists()) {
                        return nvmNpmPath;
                    }
                }
            }
        }
        
        // 检查系统路径
        for (String path : possiblePaths) {
            if (new File(path).exists()) {
                return path;
            }
        }
        
        return null;
    }

    /**
     * 操作系统检测
     *
     * @return true表示Windows系统，false表示非Windows系统
     */
    private boolean isWindows() {
        return System.getProperty("os.name").toLowerCase().contains("windows");
    }

    /**
     * 构建环境变量数组，确保包含npm路径
     *
     * @return 环境变量数组
     */
    private String[] buildEnvironment() {
        Map<String, String> env = System.getenv();
        List<String> envList = new ArrayList<>();
        
        // 复制现有环境变量
        for (Map.Entry<String, String> entry : env.entrySet()) {
            envList.add(entry.getKey() + "=" + entry.getValue());
        }
        
        // 在Linux系统下，特别处理PATH环境变量，确保包含常见的npm路径
        if (!isWindows()) {
            String currentPath = env.get("PATH");
            if (currentPath == null) {
                currentPath = "";
            }
            
            // 添加常见的npm安装路径
            List<String> npmPaths = new ArrayList<>();
            
            // NVM路径
            String homeDir = System.getProperty("user.home");
            if (homeDir != null) {
                // 检查.nvm目录下的node版本
                File nvmDir = new File(homeDir, ".nvm/versions/node");
                if (nvmDir.exists() && nvmDir.isDirectory()) {
                    File[] versionDirs = nvmDir.listFiles(File::isDirectory);
                    if (versionDirs != null && versionDirs.length > 0) {
                        // 使用最新版本的node
                        File latestVersion = versionDirs[versionDirs.length - 1];
                        npmPaths.add(latestVersion.getAbsolutePath() + "/bin");
                    }
                }
                
                // 用户本地npm路径
                npmPaths.add(homeDir + "/.npm-global/bin");
                npmPaths.add(homeDir + "/.local/bin");
            }
            
            // 系统npm路径
            npmPaths.add("/usr/local/bin");
            npmPaths.add("/usr/bin");
            npmPaths.add("/bin");
            
            // 构建新的PATH
            StringBuilder newPath = new StringBuilder();
            for (String npmPath : npmPaths) {
                if (!currentPath.contains(npmPath)) {
                    newPath.append(npmPath).append(":");
                }
            }
            newPath.append(currentPath);
            
            // 更新PATH环境变量
            boolean pathUpdated = false;
            for (int i = 0; i < envList.size(); i++) {
                if (envList.get(i).startsWith("PATH=")) {
                    envList.set(i, "PATH=" + newPath.toString());
                    pathUpdated = true;
                    break;
                }
            }
            if (!pathUpdated) {
                envList.add("PATH=" + newPath.toString());
            }
            
            log.debug("更新后的PATH: {}", newPath.toString());
        }
        
        return envList.toArray(new String[0]);
    }

    /**
     * 执行命令
     *
     * @param workingDir     工作目录
     * @param command        命令字符串
     * @param timeoutSeconds 超时时间（秒）
     * @return 是否执行成功
     */
    private boolean executeCommand(File workingDir, String command, int timeoutSeconds) {
        try {
            log.info("在目录 {} 中执行命令: {}", workingDir.getAbsolutePath(), command);
            
            // 构建环境变量，确保包含npm路径
            String[] envp = buildEnvironment();
            
            Process process = RuntimeUtil.exec(
                    envp,
                    workingDir,
                    command.split("\\s+") // 命令分割为数组
            );
            
            // 获取进程的输出流和错误流
            StringBuilder output = new StringBuilder();
            StringBuilder errorOutput = new StringBuilder();
            
            // 启动线程读取输出流
            Thread outputReader = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        output.append(line).append("\n");
                        log.debug("命令输出: {}", line);
                    }
                } catch (Exception e) {
                    log.error("读取命令输出失败", e);
                }
            });
            
            // 启动线程读取错误流
            Thread errorReader = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        errorOutput.append(line).append("\n");
                        log.error("命令错误输出: {}", line);
                    }
                } catch (Exception e) {
                    log.error("读取命令错误输出失败", e);
                }
            });
            
            outputReader.start();
            errorReader.start();
            
            // 等待进程完成，设置超时
            boolean finished = process.waitFor(timeoutSeconds, TimeUnit.SECONDS);
            if (!finished) {
                log.error("命令执行超时（{}秒），强制终止进程", timeoutSeconds);
                process.destroyForcibly();
                // 等待读取线程结束
                outputReader.interrupt();
                errorReader.interrupt();
                return false;
            }
            
            // 等待读取线程完成
            outputReader.join(1000);
            errorReader.join(1000);
            
            int exitCode = process.exitValue();
            if (exitCode == 0) {
                log.info("命令执行成功: {}", command);
                if (!output.isEmpty()) {
                    log.info("命令输出: {}", output.toString());
                }
                return true;
            } else {
                log.error("命令执行失败，退出码: {}", exitCode);
                if (!errorOutput.isEmpty()) {
                    log.error("错误详情: {}", errorOutput.toString());
                }
                return false;
            }
        } catch (Exception e) {
            log.error("执行命令失败: {}, 错误信息: {}", command, e.getMessage());
            return false;
        }
    }

}